namespace a {
  abstract class Shape {
    abstract draw (): void
  }
  
  class RectShape extends Shape {
    draw (): void {
      console.log("draw a rect")
    }
  }
  
  class CircleShape extends Shape {
    draw (): void {
      console.log("draw a circle")
    }
  }
  
  class RedDecorator extends Shape {
    constructor (public shape: Shape) {
      super()
    }
  
    draw (): void {
      this.shape.draw()
      console.log("draw red color")
    }
  }
  
  class BlueDecorator extends Shape {
    constructor (public shape: Shape) {
      super()
    }
  
    draw (): void {
      this.shape.draw()
      console.log("draw blue color")
    }
  }
  
  export function main (): void {
    let rectShape: Shape = new RectShape()
    let redRect: Shape = new RedDecorator(rectShape)
    redRect.draw()
  
    let blueRect: Shape = new BlueDecorator(rectShape)
    blueRect.draw()
  }
  
}

namespace b {

  function classDecorator<T extends { new(...args: any[]): {}}> (constructor: T) {
    console.log("class decorator enter...........")
    return class extends constructor {
      newProperty: string = "new property"
    }
  }

  function before (msg: string) {
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
      const originValue = descriptor.value
      descriptor.value = function (...args: any[]) {
        console.log("before msg: ", msg)
        originValue.apply(this, args)
      }
    }
  }
  
  @classDecorator
  class Shape {
    type: string
    
    static parent: string
    
    constructor() {
      
    }

    @before("xxxxxxxx")
    draw (): void {
      console.log((this as any).newProperty)
      console.log(this.type)
    }
  }

  export function main () {
    let shape = new Shape()
    shape.type = '0000000000000000000'
    shape.draw()
  }
}

// a.main()
b.main()